import hw2.*;
import java_cup.runtime.*;
import Common.Listing;

/**
 * Answer to question 1:
 *	Stmts
 *		--> Stmt Stmts
 *		  |	lambda
 *	Stmt
 *	 	--> lparen Call rparen
 *		  |	number
 *	Call
 *		--> negate Stmt
 *		  | plus Stmt Stmt
 *		  | minus Stmt Stmt
 *		  | times Stmt Stmt
 *		  | sum Stmts
 *		  | product Stmts
 *		  | mean Stmts
 *
 *
 * Answer to question 2:
 *	First(File) = {EOF, \n}
 *	Follow(File) = {}
 *
 *	First(Line) = {}
 *	Follow(Line) = {\n}
 *
 *	First(Stmts) = {sum, product, mean}
 *	Follow(Stmts) = {rparen}
 *
 *	First(Stmt) = {negate, plus, minus, times, sum, product, mean, \n}
 *	Follow(Stmt) = {rparen}
 *
 *	First(Call) = {lparen}
 *	Follow(Call) = {rparen}
 *
 *
 * Answer to question 4:
 * 	When the mean token is encountered, mean() is called. mean() expects a mean token
 * 	and removes it from the parser queue. Next mean enters a loop iterating through subsequent
 * 	statements summing them and incrementing a count until a rparen is peeked after a statement.
 * 	After peeking the rparen, the sum is divided by the count and the result is returned.
 *
 */

public class RecursiveDescent {

	public RecursiveDescent() {
		Scanner.init();
		Symbol t = Scanner.peek();
		while (t.sym != sym.EOF) {
			System.out.println("\n==>"+Stmt());
			t = Scanner.peek();
		}
	}
	
	private double Stmt(){
		Symbol t = Scanner.peek();
		if(t.sym == sym.number){
			//Have to use the constructor of the cast or there will be a ClassCastException
			return new Double((Integer)expect(sym.number).value);
		}else{
			expect(sym.lparen);
			double result = Call();
			expect(sym.rparen);
			return result;
		}
	}

	
	private double Call(){
		Symbol t = Scanner.peek();
		switch(t.sym){
		case sym.negate:
			return negate();
		case sym.plus:
			return plus();
		case sym.minus:
			return minus();
		case sym.times:
			return times();
		case sym.sum:
			return sum();
		case sym.product:
			return product();
		case sym.mean:
			return mean();
		default:
			fail("Call opened, but not a valid symbol type for call: "+t.sym+"\nABORT");
			return 0;
		}
	}
	
	private double negate(){
		expect(sym.negate);
		return -Stmt();
	}
	
	private double plus(){
		expect(sym.plus);
		return Stmt() + Stmt();
	}
	
	private double minus(){
		expect(sym.minus);
		return Stmt() - Stmt();
	}
	
	private double times(){
		expect(sym.times);
		return Stmt() * Stmt();
	}
	
	private double sum(){
		expect(sym.sum);
		double sum = 0.;
		Symbol t = Scanner.peek();
		while(t.sym == sym.lparen || t.sym == sym.number){
			sum += Stmt();
			t = Scanner.peek();
		}
		return sum;
	}
	
	private double product(){
		expect(sym.product);
		double product = 1.;
		Symbol t = Scanner.peek();
		while(t.sym == sym.lparen || t.sym == sym.number){
			product *= Stmt();
			t = Scanner.peek();
		}
		return product;
	}
	
	private double mean(){
		expect(sym.mean);
		int count = 0;
		double sum = 0.;
		Symbol t = Scanner.peek();
		while(t.sym == sym.lparen || t.sym == sym.number){
			sum += Stmt();
			++count;
			t = Scanner.peek();
		}
		return sum/count;
	}

	void fail(String s) {
		System.err.println("Error: " + s);
		System.exit(-1);
	}

	private Symbol expect(int type){
		Symbol t = Scanner.peek();
		if(t.sym != type){
			fail("Incorrect symbol\nExpected type: "+type+"\nFound type: "+t.sym+"\nABORT");
		}
		Scanner.advance();
		return t;
	}
}
